#include <stdio.h>   /* */
#include <stdlib.h>  /* exit() */
#include <string.h>  /* memset(), memcpy() */
#include <sys/utsname.h>   /* uname() */
#include <sys/types.h>
#include <sys/socket.h>   /* socket(), bind(),
                             listen(), accept() */
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>  /* fork(), write(), close() */
#include <time.h>

#include "io.c"

#define BACKLOG 5

#define OPR_READ 1
#define OPR_WRITE 2
#define OPR_END 3
	
int main(int argc,char **argv) {

	struct sockaddr_in addr_cliente;
	int len_inet;
	char *port;
	struct addrinfo hints, *res, *p;
	int sockfd, connfd;
	int counter = 0;
	
	//datos
	int oper = 0;
	char lu[6], nota[3], *resp;
	reg r;
	memset(&r,0,sizeof(reg));
	resp = malloc ( 100 );
	
	
	//informacion de la direccion del servidor
	if(argc!=2){
		printf("No ha ingresado el numero de puerto\n");
		return 1;
	}
	port=argv[1];

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;

   	if (getaddrinfo(NULL, port, &hints, &res) != 0){
   		perror("getaddrinfo");
   		exit(1);
   	}

   	for(p = res;p != NULL; p = p->ai_next) {
        void *addr;
        char ipstr[INET_ADDRSTRLEN];

        struct sockaddr_in *an_ipv4_addr = (struct sockaddr_in *)p->ai_addr;
        addr = &(an_ipv4_addr->sin_addr);

        // convert the IP to a string and print it:
        inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
        printf(" %s:%d \n",  ipstr, an_ipv4_addr->sin_port);

        /* creo el socket */
		if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
			perror("server: socket");
			continue;
		}

        if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
            close(sockfd);
            perror("server: bind");
            continue;
        }

    }
    
    freeaddrinfo(res); // all done with this structure

    /* make it a listening socket. BACKLOG es un advice y cambio desde 2.2 */
    if (listen(sockfd, BACKLOG) == -1) {
        perror("listen");
        exit(1);
    }


	while(1) {
		/** Wait for a connect:*/
		len_inet = sizeof addr_cliente;
		connfd = accept(sockfd, (struct sockaddr *)&addr_cliente, (socklen_t*) &len_inet);

		if ( connfd == -1 )
			perror("error Aceptando...");
			
		counter++;
		
		printf("Conección aceptada desde: %s\n", inet_ntoa(addr_cliente.sin_addr));
		
		if(read(connfd, &oper, sizeof(int)) < 0)
			perror("error leyendo el mensaje");
			
		for(; oper != OPR_END; read(connfd, &oper, sizeof(int))) {
			switch(oper) {
				case OPR_READ:
					read(connfd, lu, 5 * sizeof(char));
					lu[5] = '\0';
					printf("LU: %s\n", lu);
					switch(getNota(lu, &r)) {
					case 0:
						sprintf(resp, "%s | %s | ", r.id, r.nombre);
						if(strcmp(r.nota, "-") == 0)
							resp = strcat(resp, "<no_asignada>");
						else
							resp=strcat(resp,r.nota);
						break;
					case -1:
						sprintf(resp, "Alumno con LU%s no existe.", lu);
						break;
					case -2:
						sprintf(resp, "Error al leer la base de datos.");
						break;
					}
					strcat(resp, "\0");
					printf("Respuesta: %s.\n", resp);
					write(connfd,resp,strlen(resp)*sizeof(char));
					break;
					
				case OPR_WRITE:
					read(connfd, lu, 5 * sizeof(char));
					lu[5] = '\0';
					printf("LU: %s\n", lu);
					read(connfd, nota, sizeof(nota));
					printf("Nota: %s\n", nota);
					switch(getNota(lu, &r)) {
					case 0:
						if(strcmp(r.nota, "-") == 0) {
							setNota(lu, nota);
							sprintf(resp, "%s | %s | %s", r.id, r.nombre, nota);
						} else
							sprintf(resp, "Alumno con LU%s ya tiene nota asignada.", lu);
						break;
					case -1:
						sprintf(resp, "Alumno con LU%s no existe.", lu);
						break;
					case -2:
						sprintf(resp, "Error al leer la base de datos.");
						break;
					}
					strcat(resp, "\0");
					printf("Respuesta: %s.\n", resp);
					write(connfd,resp,sizeof(resp));
					break;
			}
		}
		printf("Cerrando coneccion con: %s\n", inet_ntoa(addr_cliente.sin_addr));
		close(connfd); /* cerrar socket */
	} /* end while */

	close(sockfd);
	return 0;
}
